package service

import (
	"context"
	"errors"
	"fmt"
	"math"
	"mini-app/config"
	"mini-app/config/mysql"
	"mini-app/internal/app/grpc/client"
	"mini-app/internal/app/grpc/common"
	"mini-app/internal/app/grpc/protoc/devicec"
	"mini-app/internal/app/grpc/protoc/sale_adminc"
	"mini-app/internal/dal"
	"mini-app/internal/dao"
	"mini-app/internal/define"
	"mini-app/internal/models"
	"mini-app/internal/pkg"
	"sort"
	"strconv"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/go-pay/gopay/wechat/v3"
	"github.com/samber/lo"
	"github.com/shopspring/decimal"
	"github.com/silenceper/wechat/v2/miniprogram/order"
)

type Order struct{}

type CartRobotGoods struct {
	RobotID string `json:"robot_id"` // 机器名称
	GoodsID string `json:"goods_id"` // 商品id
	Count   int    `json:"count"`    // 商品数量
}

type CreateOrderRes struct {
	BatchID string   `json:"batch_id"`
	ID      []string `json:"id"`
}

// CreateOrder 创建订单
// 一种商品一个订单
func (Order) CreateOrder(
	ctx context.Context,
	openid string,
	cartList []CartRobotGoods,
	cart int,
	purchaseMethod int,
	userAddress models.UserAddress,
	consumeMethod define.ConsumeMethodDef,
	code string,
	updateOrderStatus int,
) (ids []CreateOrderRes, err error) {

	if purchaseMethod == 0 {
		purchaseMethod = 1
	}
	if purchaseMethod != 1 && purchaseMethod != 2 {
		return nil, errorx.New("无效的购买方式", -1)
	}

	saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
	if saleAdminClientErr != nil {
		err = errorx.New("获取零售服务失败", -1)
		logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
		return nil, err
	}

	// 快递到家时，地址必填
	if purchaseMethod == 2 {
		if userAddress.UserName == "" {
			return nil, errorx.New("收货人姓名不能为空", -1)
		}
		if userAddress.TelNumber == "" {
			return nil, errorx.New("收货人手机号不能为空", -1)
		}
		if userAddress.ProvinceName == "" {
			return nil, errorx.New("收货省份不能为空", -1)
		}
		if userAddress.CityName == "" {
			return nil, errorx.New("收货城市不能为空", -1)
		}
		if userAddress.DetailAddress == "" {
			return nil, errorx.New("收货详细地址不能空", -1)
		}
	}

	// 参数校验
	if consumeMethod == define.ConsumeMethodRedemptionCode && code == "" {
		err = errorx.New("兑换码未填", -1)
		return nil, err
	}

	// 购物车商品数量校验
	if len(cartList) == 0 {
		err = errorx.New("购物车不能为空", -1)
		return
	}

	// 兑换码
	var redemptionCodeInfo *sale_adminc.GetRedemptionCodeByCodeRsp_RedemptionCodeData
	if consumeMethod == define.ConsumeMethodRedemptionCode {
		if code == "" {
			err = errorx.New("兑换码未填", -1)
			return
		}
		// 兑换码信息
		redemptionCodeRsp, redemptionCodeInfoErr := saleAdminClient.GetRedemptionCodeByCode(ctx, &sale_adminc.GetRedemptionCodeByCodeReq{
			Code: code,
		})

		if redemptionCodeInfoErr != nil {
			logx.Error(ctx, "connect sale admin grpc error", logx.Any("redemptionCodeInfoErr", redemptionCodeInfoErr))
			err = errorx.New("兑换码信息查询失败", -1)
			return
		}
		if redemptionCodeRsp == nil || redemptionCodeRsp.Code != 0 {
			logx.Error(ctx, "redemptionCodeRsp error")
			if redemptionCodeRsp == nil {
				err = errorx.New("兑换码信息查询错误", -1)
			} else {
				err = errorx.New(fmt.Sprintf("兑换码信息查询错误: %s",
					redemptionCodeRsp.Msg))
			}

			return
		}
		if redemptionCodeRsp.Data == nil {
			logx.Error(ctx, "redemptionCodeRsp Data error")
			err = errorx.New("兑换码Data信息错误", -1)
			return
		}

		redemptionCodeInfo = redemptionCodeRsp.Data
	}

	var robotGoodsCount = make(map[string]int) // 机器 - 商品数量
	for _, v := range cartList {
		robotGoodsCount[v.RobotID] += v.Count
		if robotGoodsCount[v.RobotID] > 3 {
			err = errorx.New("同一机器，商品数量最多不能超过3个", -1)
			return
		}
	}

	// 机器商品信息
	var robotGoodsStock = make(map[string]map[string]int)
	var robotIDList []string
	var robotCartList = make(map[string][]CartRobotGoods)
	for _, v := range cartList {
		robotIDList = append(robotIDList, v.RobotID)
		robotCartList[v.RobotID] = append(robotCartList[v.RobotID], v)
	}
	robotIDList = lo.Uniq(robotIDList)
	for _, robotName := range robotIDList {
		item := make(map[string]int)
		// 机器上的商品库存
		robotGoodsStockRsp, robotGoodsStockErr := saleAdminClient.GoodsUsableStock(ctx, &sale_adminc.GoodsUsableStockReq{
			RobotId: robotName,
		})
		if robotGoodsStockErr != nil || robotGoodsStockRsp == nil {
			err = errorx.New("获取商品库存信息错误", -1)
			return
		}
		for _, v := range robotGoodsStockRsp.Data {
			if _, ok := item[v.GoodsId]; !ok { // 不存在则赋值
				item[v.GoodsId] = int(v.Stock)
			} else {
				if int(v.Stock) > item[v.GoodsId] { // 存在取库存最大的货道的库存
					item[v.GoodsId] = int(v.Stock)
				}
			}
		}
		robotGoodsStock[robotName] = item
	}

	for _, robotGoodsList := range robotCartList {
		batchID, _ := helper.UUID{}.String() // 一次下单，同一机器共用一个batch_id
		batchOrders := []string{}
		for _, v := range robotGoodsList {
			if v.GoodsID == "" {
				err = errorx.New("商品ID为空", -1)
			}

			var goodsExpressFee float64
			goodsInfo, _ := saleAdminClient.GoodsDetail(ctx, &sale_adminc.GoodsDetailReq{
				GoodsId: v.GoodsID,
			})

			if goodsInfo == nil {
				err = errorx.New("未找到商品信息", -1)
				return nil, err
			}
			// 商品价格校验，价格异常时提醒
			if goodsInfo.GoodsType == 6 && goodsInfo.GoodsPrice != 0 {
				err = errorx.New("0元购商品价格错误", -1)
				return
			}

			// 机器上是否存在该商品
			if _, ok := robotGoodsStock[v.RobotID]; !ok {
				err = errorx.New("机器上暂无商品", -1)
				return
			}
			if _, ok := robotGoodsStock[v.RobotID][v.GoodsID]; !ok {
				err = errorx.New("机器上不存在该商品", -1)
				return
			}

			// 机器上的商品库存校验
			if robotGoodsStock[v.RobotID][v.GoodsID] < v.Count {
				err = errorx.New("机器上商品库存不足", -1)
				return
			}

			var orderStatus int
			if goodsInfo.GoodsType == define.GoodsTypeZeroPrice { // 6 0元购商品
				orderStatus = define.OrderStatusToPickUp
				// 0元购商品限制单人仅限购一单
				var GoodsCount int64
				mysql.NewDB().Model(&models.Order{}).
					WithContext(ctx).
					Where("openid = ?", openid).
					Where("goods_id = ?", v.GoodsID).
					Where("goods_type = 6").
					Count(&GoodsCount)
				if GoodsCount > 0 {
					err = errorx.New("仅限领取一次免费商品", -1)
					return
				}
			} else if updateOrderStatus != 0 {
				orderStatus = updateOrderStatus
			} else {
				orderStatus = define.OrderStatusUnpaid
			}
			// 计算运费
			if purchaseMethod == 2 {
				expreeFee, getExpressFeeErr := common.GRPC{}.GetExpressFee(ctx, v.GoodsID, userAddress.ProvinceName)
				if getExpressFeeErr != nil {
					logx.Error(context.Background(), "get ExpressFee error", logx.Err(getExpressFeeErr), logx.Any("userAddress", userAddress))
					err = getExpressFeeErr
					return
				}
				// 每个机器的订单一个运费，取最贵的
				if expreeFee > goodsExpressFee {
					goodsExpressFee = expreeFee
				}
			}

			// 兑换码校验
			if redemptionCodeInfo != nil {
				if goodsInfo.GoodsType == define.GoodsTypeGuide && goodsInfo.GoodsPrice > 0 {
					// 请使用RedeemOrder核销价格非0的导流商品的兑换码
					return nil, errorx.New("此接口不支持价格非0的导流商品使用兑换码", -1)
				}
				if redemptionCodeInfo.RedemptionState == 0 {
					return nil, errorx.New("兑换码失效", -1)
				}

				if redemptionCodeInfo.RedemptionState == 2 {
					return nil, errorx.New("兑换码已使用", -1)
				}

				//var checkCodeCond bool = true //监测是否goods是否满足兑换码兑换商品的条件
				// 检查是否goods是否满足兑换码兑换商品的条件
				if redemptionCodeInfo.CouponBindType == string(define.CouponBindTypeGoods) {
					goodsID, _ := helper.StringToInt64(goodsInfo.GoodsId)
					if !lo.Contains(redemptionCodeInfo.GoodsIds, goodsID) {
						return nil, errorx.New("兑换码兑换商品条件不满足，商品ID不满足", -1)
					}
				} else if redemptionCodeInfo.CouponBindType == string(define.CouponBindTypeGoodsClass) {
					classID, _ := helper.StringToInt64(goodsInfo.ClassId)
					if !lo.Contains(redemptionCodeInfo.GoodsClassIds, classID) {
						return nil, errorx.New("兑换码兑换商品条件不满足, 商品分类不满足", -1)
					}
				} else if redemptionCodeInfo.CouponBindType == string(define.CouponBindTypeActivity) {
					onlineEventDetail := dao.OnlineEvent{}.Detail(ctx, mysql.NewDB(), helper.Int64ToString(redemptionCodeInfo.OnlineEventId))
					if onlineEventDetail == nil || onlineEventDetail.DeletedAt != 0 {
						return nil, errorx.New("兑换码兑换商品条件不满足, 活动失效或不存在", -1)
					}

					if onlineEventDetail.Status == 2 {
						return nil, errorx.New("兑换码兑换商品条件不满足, 活动未开始", -1)
					}

					if onlineEventDetail.Status == 3 {
						return nil, errorx.New("兑换码兑换商品条件不满足, 活动已结束", -1)
					}

					if onlineEventDetail.Status != 1 {
						logx.Error(ctx, "onlineEventDetail Status error", logx.Any("status", onlineEventDetail.Status))
						return nil, errorx.New("兑换码兑换商品条件不满足, 活动未开始或已结束", -1)
					}

					if goodsInfo.GoodsId != onlineEventDetail.GoodsID {
						return nil, errorx.New("兑换码兑换商品条件不满足, 对应活动的商品ID不匹配", -1)
					}
				} else {
					logx.Error(ctx, "onlineEventDetail Status error",
						logx.Any("redemptionCodeInfo", redemptionCodeInfo))
					return nil, errorx.New("兑换码兑换商品条件不满足, 兑换码绑定类型错误", -1)
				}

				orderStatus = define.OrderStatusToPickUp

				consumeRsp, consumeErr := saleAdminClient.ConsumeRedemptionCode(ctx, &sale_adminc.ConsumeRedemptionCodeReq{
					Code:    code,
					GoodsId: goodsInfo.GoodsId,
				})
				if consumeErr != nil || consumeRsp == nil {
					logx.Error(ctx, "consume redemption code request error", logx.Err(consumeErr))
					return nil, errorx.New(errors.Join(fmt.Errorf("兑换码核销请求失败"), consumeErr).Error(), -1)
				}

				if consumeRsp.Code != 0 {
					logx.Error(ctx, "consume redemption code error", logx.Any("msg", consumeRsp.Msg))
					return nil, errorx.New(fmt.Sprintf("兑换码核销失败, %s", fmt.Errorf(consumeRsp.Msg)), -1)
				}

			}

			// 写入订单表
			ID, _ := helper.UUID{}.String()
			dao.Order{}.Create(
				ctx,
				dal.Q,
				openid,
				ID,
				batchID,
				v.RobotID,
				orderStatus,
				v.GoodsID,
				int(goodsInfo.GoodsType),
				goodsInfo.GoodsName,
				v.Count,
				goodsInfo.GoodsPrice,
				float64(v.Count)*goodsInfo.GoodsPrice,
				0,
				goodsExpressFee,
				purchaseMethod,
				"",
				userAddress,
			)
			if cart == 1 { // 删除购物车
				dao.ShoppingCart{}.Reduce(ctx, dal.Q, openid, v.RobotID, v.GoodsID, v.Count)
			}
			batchOrders = append(batchOrders, ID)
		}
		ids = append(ids, CreateOrderRes{
			BatchID: batchID,
			ID:      batchOrders,
		})
	}

	return
}

func (t Order) RedeemOrder(
	ctx context.Context,
	openID string,
	orderID string,
	code string,
) (err error) {

	// 校验订单
	orderDetail, err := dao.Order{}.Detail(ctx, dal.Q, openID, orderID, "", "", "", 0)
	if err != nil {
		return errorx.New(fmt.Sprintf("查询订单错误：%s", err.Error()))
	}

	if orderDetail == nil {
		err = errorx.New("订单不存在", -1)
		return
	}

	if orderDetail.GoodsType != define.GoodsTypeGuide || orderDetail.Amount <= 0 {
		// 当前版本Univalent和Amount的值是一样的，这里先使用Amount来判断
		err = errorx.New("该类商品不支持此兑换方式", -1)
		return
	}

	if orderDetail.OrderStatus != define.OrderStatusPainAndToRedeem {
		if orderDetail.OrderStatus == define.OrderStatusToPickUp {
			err = errorx.New(fmt.Sprintf("订单已兑换过，请到订单列表领取兑换码"), -1)
		} else {
			err = errorx.New(fmt.Sprintf("订单不在待兑换状态(%d)(请确认是否支付过)或该类商品不支持兑换", orderDetail.OrderStatus), -1)
		}

		return
	}

	// 兑换码
	{
		saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
		if saleAdminClientErr != nil {
			err = errorx.New("获取零售服务失败", -1)
			logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
			return err
		}

		// 兑换码信息
		redemptionCodeRsp, redemptionCodeInfoErr := saleAdminClient.GetRedemptionCodeByCode(ctx, &sale_adminc.GetRedemptionCodeByCodeReq{
			Code: code,
		})

		if redemptionCodeInfoErr != nil {
			logx.Error(ctx, "connect sale admin grpc error", logx.Any("redemptionCodeInfoErr", redemptionCodeInfoErr))
			err = errorx.New("兑换码信息查询失败", -1)
			return err
		}
		if redemptionCodeRsp == nil || redemptionCodeRsp.Code != 0 {
			logx.Error(ctx, "redemptionCodeRsp error")
			if redemptionCodeRsp == nil {
				err = errorx.New("兑换码信息查询错误", -1)
			} else {
				err = errorx.New(fmt.Sprintf("兑换码信息查询错误: %s",
					redemptionCodeRsp.Msg))
			}

			return err
		}
		if redemptionCodeRsp.Data == nil {
			logx.Error(ctx, "redemptionCodeRsp Data error")
			err = errorx.New("兑换码Data信息错误", -1)
			return err
		}

		//redemptionCodeInfo := redemptionCodeRsp.Data

		consumeRsp, consumeErr := saleAdminClient.ConsumeRedemptionCode(ctx, &sale_adminc.ConsumeRedemptionCodeReq{
			Code:       code,
			GoodsId:    orderDetail.GoodsID,
			OutTradeNo: orderDetail.BatchID,
		})
		if consumeErr != nil || consumeRsp == nil {
			logx.Error(ctx, "consume redemption code request error", logx.Err(consumeErr))
			return errorx.New(errors.Join(fmt.Errorf("兑换码核销请求失败"), consumeErr).Error(), -1)
		}

		if consumeRsp.Code != 0 {
			logx.Error(ctx, "consume redemption code error", logx.Any("msg", consumeRsp.Msg))
			return errorx.New(fmt.Sprintf("兑换码核销失败, %s", fmt.Errorf(consumeRsp.Msg)), -1)
		}

		// 更新订单状态
		err := dao.Order{}.OrderStatusChange(ctx, dal.Q, orderID, "", define.OrderStatusToPickUp)
		if err != nil {
			logx.Error(ctx, "update order status error", logx.Err(err))
			return errorx.New("更新订单状态失败", -1)
		}

		if orderDetail.PurchaseMethod == 1 { // 自提订单
			// 2025-10-16 有价格的导流商品，需要支付和兑换码两个环节，在核销兑换码后，发送微信发货消息
			// TODO 这个考虑是取货时发还是在支付完成或兑换码完成时
			err = t.UploadShippingInfo(orderDetail.Openid, orderDetail.BatchID)
			if err != nil {
				logx.Error(ctx, "upload shipping info error", logx.Err(err))
			}
		}
	}

	return nil

}

type OrderListT struct {
	models.Order
	PayDeadline int64 `json:"pay_deadline"` // 支付截止时间
}

// 订单列表
func (Order) OrderList(ctx context.Context, openid string, orderStatus, page, limit int, goodsIDs []string) (
	[]OrderListT, int64, error,
) {
	list := []OrderListT{}
	orderRecords, total, err := dao.Order{}.OrderList(ctx, dal.Q, openid, "", "", "", helper.JoinComma(goodsIDs), orderStatus, page, limit)
	for k, order := range orderRecords {
		// 获取机器商品图片
		goodsList, _ := common.GRPC{}.GetGoodsList(ctx, order.RobotID, 0, 0, order.GoodsID, "", 0, 0)
		if len(goodsList) > 0 {
			orderRecords[k].GoodsImage = goodsList[0].GoodsImage
		}
		// 获取活动信息
		if order.OrderStatus == define.OrderStatusApplyingPending && order.OnlineEventID != "" {
			eventDetail := dao.OnlineEvent{}.Detail(ctx, mysql.NewDB(), order.OnlineEventID)
			if eventDetail != nil {
				orderRecords[k].OnlineEventEndTime = eventDetail.EndTime
			}
		}
	}
	// 机器商品图片为空，则使用商品原图片
	// 主要针对活动商品
	for index, record := range orderRecords {
		if record.GoodsImage == "" {
			rsp := common.GRPC{}.GoodsDetail(ctx, record.GoodsID)
			if rsp != nil {
				orderRecords[index].GoodsImage = rsp.GoodsImage
			}
		}
		if record.CompletedAt == 0 {
			record.CompletedAt = record.PickupDeadline
		}
	}
	// 排序
	sort.Slice(orderRecords, func(i, j int) bool {
		if orderRecords[i].CreatedAt == orderRecords[j].CreatedAt {
			// 如果创建时间相同，则按照 BatchID 和 RobotID 排序作为次要条件
			batchIDi, _ := strconv.Atoi(orderRecords[i].BatchID)
			batchIDj, _ := strconv.Atoi(orderRecords[j].BatchID)
			if batchIDi == batchIDj {
				return orderRecords[i].RobotID > orderRecords[j].RobotID
			}
			return batchIDi > batchIDj
		}
		return orderRecords[i].CreatedAt > orderRecords[j].CreatedAt // CreatedAt 降序排序
	})
	// 未支付的订单
	// 活动订单超过一周，订单状态为已关闭
	// 普通订单超过15分钟未支付的订单，订单状态为已关闭
	for index, record := range orderRecords {
		if record.OrderStatus == define.AdOrderStatusUnpaid {
			if record.OnlineEventID != "" && record.CreatedAt < time.Now().Unix()-3600*24*7 {
				orderRecords[index].OrderStatus = define.OrderStatusClosed

			} else if record.OnlineEventID == "" && record.CreatedAt < time.Now().Unix()-15*60 {
				orderRecords[index].OrderStatus = define.OrderStatusClosed
			}
		}
	}
	// 订单支付截止时间
	for _, record := range orderRecords {
		var payDeadline int64 = 0
		if record.OnlineEventID == "" {
			payDeadline = record.CreatedAt + 15*60
		} else {
			payDeadline = record.OnlineEventEndTime + 3600*24*7
		}
		list = append(list, OrderListT{
			Order:       *record,
			PayDeadline: payDeadline,
		})
	}
	return list, total, err
}

type OrderDetailT struct {
	*models.Order
	ExpressNum   string                      `json:"express_num"`
	ExpressCom   string                      `json:"express_com"`
	PayDeadline  int64                       `json:"pay_deadline"` // 支付截止时间
	GoodsDetails *sale_adminc.GoodsDetailRsp `json:"goods_details,omitempty"`
}

// 订单详情
func (Order) OrderDetail(ctx context.Context, openid, ID string) (detail *OrderDetailT, err error) {
	data, err := dao.Order{}.Detail(ctx, dal.Q, openid, ID, "", "", "", 0)
	detail = &OrderDetailT{}
	if err == nil {
		// 获取机器商品图片
		goodsList, _ := common.GRPC{}.GetGoodsList(ctx, data.RobotID, 0, 0, data.GoodsID, "", 0, 0)
		if len(goodsList) > 0 {
			data.GoodsImage = goodsList[0].GoodsImage
		}
		// 没有设置机器商品图片的，取商品原图，主要针对活动商品
		if data.GoodsImage == "" {
			rsp := common.GRPC{}.GoodsDetail(ctx, data.GoodsID)
			if rsp != nil {
				data.GoodsImage = rsp.GoodsImage
			}
		}
		saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
		if saleAdminClientErr != nil {
			logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
			return detail, errorx.New("获取商品信息失败", -1)
		}
		goodsInfo, _ := saleAdminClient.GoodsDetail(ctx, &sale_adminc.GoodsDetailReq{
			GoodsId: data.GoodsID,
		})
		if goodsInfo != nil {
			data.DeductionLimit = goodsInfo.DeductionLimit

			if goodsInfo.GoodsType == define.GoodsTypeGuide {
				// 现在先只是有几个的导流商品才返回商品详情，后续如果其它类型需要，再放开限制
				detail.GoodsDetails = goodsInfo
			}
		}

		deviceClient, deviceClientErr := client.GetAdminClient()
		if deviceClientErr == nil {
			// 获取设备详情
			deviceInfo, DeviceErr := deviceClient.DeviceDetail(ctx, &devicec.DeviceDetailReq{RobotId: data.RobotID})
			if DeviceErr == nil && deviceInfo.Code == 0 {
				data.RobotFloor = deviceInfo.Device.Floor
				// 获取位置信息
				req := &devicec.LocationDetailReq{
					LocationId: deviceInfo.Device.LocationId,
				}
				locationDetail, locationErr := deviceClient.LocationDetail(ctx, req)
				if locationErr == nil && locationDetail.Location != nil {
					data.RobotLocation = locationDetail.Location.Name
					data.RobotAddr = locationDetail.Location.Addr
					data.RobotLongitude = locationDetail.Location.Lng
					data.RobotLatitude = locationDetail.Location.Lat
				}
			}
		}
	} else {
		return nil, errorx.New("订单不存在", -1)
	}
	detail.Order = data
	// 活动信息
	eventDetail := dao.OnlineEvent{}.Detail(ctx, mysql.NewDB(), detail.OnlineEventID)
	if eventDetail != nil {
		detail.OnlineEventEndTime = eventDetail.EndTime
	}
	// 物流信息
	expressNum, expressCom := common.GRPC{}.OrderShipmentInfo(ctx, data.BatchID)
	detail.ExpressNum = expressNum
	detail.ExpressCom = expressCom
	// 订单已关闭状态
	if detail.OrderStatus == define.AdOrderStatusUnpaid {
		if detail.OnlineEventID != "" && detail.CreatedAt < time.Now().Unix()-3600*24*7 {
			detail.OrderStatus = define.OrderStatusClosed

		} else if detail.OnlineEventID == "" && detail.CreatedAt < time.Now().Unix()-15*60 {
			detail.OrderStatus = define.OrderStatusClosed
		}
	}
	// 支付截止时间
	if detail.OnlineEventID == "" {
		detail.PayDeadline = detail.CreatedAt + 15*60
	} else { // 活动订单
		if detail.OnlineEventEndTime > 0 {
			detail.PayDeadline = eventDetail.EndTime + 3600*24*7
		} else {
			detail.PayDeadline = detail.CreatedAt + 15*60
		}
	}

	return
}

type PickUpOrder struct {
	BatchID        string             `json:"batch_id"`
	RobotID        string             `json:"robot_id"`
	Location       string             `json:"location"`
	Floor          string             `json:"floor"`
	Longitude      float64            `json:"longitude"`
	Latitude       float64            `json:"latitude"`
	Address        string             `json:"address"`
	PickupCode     string             `json:"pickup_code"`
	PickupDeadline int64              `json:"pickup_deadline"`
	PickupStatus   int64              `json:"pickup_status"`
	CreatedAt      int64              `json:"created_at"`
	GoodsList      []PickUpOrderGoods `json:"goods_list"`
}
type PickUpOrderGoods struct {
	OrderID    string  `json:"order_id"`
	GoodsID    string  `json:"goods_id"`
	GoodsName  string  `json:"goods_name"`
	GoodsImage string  `json:"goods_image"`
	Univalent  float64 `json:"univalent"`
	Count      int     `json:"count"`
}

// 取货订单列表【按机器汇总】
func (Order) PickupOrders(ctx context.Context, openid, salesID string) (data []PickUpOrder, err error) {
	deviceClient, deviceClientErr := client.GetAdminClient()
	if deviceClientErr != nil {
		logx.Error(ctx, "connect device grpc error", logx.Any("deviceClientErr", deviceClientErr))
		return
	}
	var robotID string
	if salesID != "" {
		deviceDetail, deviceDetailErr := deviceClient.DeviceDetail(ctx, &devicec.DeviceDetailReq{
			IdentificationCode: salesID,
		})
		if deviceDetailErr == nil && deviceDetail.Device != nil {
			robotID = deviceDetail.Device.RobotId
		}
	}
	// 获取所有待取货订单列表
	list, _, _ := dao.Order{}.OrderList(ctx, dal.Q, openid, "", "", robotID, "", define.OrderStatusToPickUp, -1, -1)

	// 不同批次订单分割
	var batchOrders = make(map[string][]*models.Order)
	for _, v := range list {
		batchOrders[v.BatchID] = append(batchOrders[v.BatchID], v)
	}
	for batchID, batchOrder := range batchOrders {
		// 同批次不同机器订单分割
		var robotOrders = make(map[string][]*models.Order)
		for _, v := range batchOrder {
			robotOrders[v.RobotID] = append(robotOrders[v.RobotID], v)
		}
		for robotID, robotOrderList := range robotOrders {
			var item PickUpOrder
			item.BatchID = batchID
			item.RobotID = robotID
			item.CreatedAt = robotOrderList[0].CreatedAt
			for _, v := range robotOrderList {
				if v.PickupStatus == 0 {
					item.PickupDeadline = robotOrderList[0].PickupDeadline
					item.PickupCode = robotOrderList[0].PickupCode
				}
			}

			deviceDetail, deviceErr := deviceClient.DeviceDetail(ctx, &devicec.DeviceDetailReq{
				RobotId: robotID,
			})
			if deviceErr == nil && deviceDetail.Code == 0 {
				item.Floor = deviceDetail.Device.Floor
				if deviceDetail.Device.LocationId != "" && deviceDetail.Device.LocationId != "0" {
					// 获取位置信息
					req := &devicec.LocationDetailReq{
						LocationId: deviceDetail.Device.LocationId,
					}
					locationDetail, locationErr := deviceClient.LocationDetail(ctx, req)
					if locationErr == nil && locationDetail.Location != nil {
						item.Location = locationDetail.Location.Name
						item.Longitude = float64(locationDetail.Location.Lng)
						item.Latitude = float64(locationDetail.Location.Lat)
					}
				}
			}
			// 同批次订单的【机器 -> 商品列表】
			var robotGoodsList []PickUpOrderGoods
			for _, v := range robotOrderList {
				i := PickUpOrderGoods{
					GoodsID:   v.GoodsID,
					GoodsName: v.GoodsName,
					Count:     v.Count,
					Univalent: v.Univalent,
				}
				goodsDetail := common.GRPC{}.GoodsDetail(ctx, v.GoodsID)
				if goodsDetail != nil {
					i.GoodsImage = goodsDetail.GoodsImage
				}
				robotGoodsList = append(robotGoodsList, i)
			}
			item.GoodsList = robotGoodsList
			data = append(data, item)
		}
	}
	// 排序
	sort.Slice(data, func(i, j int) bool {
		batchIDi, _ := strconv.Atoi(data[i].BatchID)
		batchIDj, _ := strconv.Atoi(data[j].BatchID)
		if batchIDi == batchIDj {
			return data[i].RobotID > data[j].RobotID
		} else {
			return batchIDi > batchIDj
		}
	})
	return
}

// 取货订单详情
func (Order) PickupOrderDetail(ctx context.Context, openid, BatchID string) (data PickUpOrder, err error) {
	deviceClient, deviceClientErr := client.GetAdminClient()
	if deviceClientErr != nil {
		logx.Error(ctx, "connect device grpc error", logx.Any("deviceClientErr", deviceClientErr))
		return
	}

	// 获取相同主订单号的所有待取货订单列表
	list, _, _ := dao.Order{}.OrderList(ctx, dal.Q, openid, "", BatchID, "", "",
		define.OrderStatusToPickUp, -1, -1)

	var robotID string
	// 不同批次订单分割
	for _, v := range list {
		data.BatchID = v.BatchID
		data.RobotID = v.RobotID
		data.PickupStatus = v.PickupStatus
		data.CreatedAt = v.CreatedAt
		if v.PickupStatus == 0 {
			data.PickupDeadline = v.PickupDeadline
			data.PickupCode = v.PickupCode
			robotID = v.RobotID
		}
	}
	if robotID != "" {
		// 获取设备详情
		deviceInfo, DeviceErr := deviceClient.DeviceDetail(ctx, &devicec.DeviceDetailReq{RobotId: robotID})
		if DeviceErr != nil || deviceInfo.Code != 0 {
			return
		}
		data.Floor = deviceInfo.Device.Floor
		// 获取位置信息
		req := &devicec.LocationDetailReq{
			LocationId: deviceInfo.Device.LocationId,
		}
		locationDetail, locationErr := deviceClient.LocationDetail(ctx, req)
		if locationErr != nil || locationDetail.Location == nil {
			logx.Error(ctx, "get location detail error", logx.Any("locationErr", locationErr))
			return
		}
		data.Location = locationDetail.Location.Name
		data.Latitude = float64(locationDetail.Location.Lat)
		data.Longitude = float64(locationDetail.Location.Lng)
		data.Address = locationDetail.Location.Addr
	}
	// 获取机器商品列表
	goodsList, _ := common.GRPC{}.GetGoodsList(ctx, data.RobotID, 0, 0, "", "", 0, 0)
	var goodsMap = make(map[string]string)
	for _, v := range goodsList {
		goodsMap[v.GoodsId] = v.GoodsImage
	}

	for _, v := range list {
		// 同批次订单的【机器 -> 商品列表】
		var robotGoodsList []PickUpOrderGoods
		i := PickUpOrderGoods{
			OrderID:   v.ID,
			GoodsID:   v.GoodsID,
			GoodsName: v.GoodsName,
			Count:     v.Count,
			Univalent: v.Univalent,
		}
		if _, ok := goodsMap[v.GoodsID]; ok {
			i.GoodsImage = goodsMap[v.GoodsID]
		}
		data.GoodsList = append(robotGoodsList, i)
	}
	return
}

type PickupInfo struct {
	PickupDeadline int64  `json:"pickup_deadline"`
	PickupCode     string `json:"pickup_code"`
}

// Pickup 取货
// 未传商品id时取所有商品
func (Order) Pickup(ctx context.Context, remoteIP, openid, batchID, robotID, goodsIDs string) (data PickupInfo, err error) {
	logx.Error(ctx, "mini-app Pickup", logx.Any("Pickup", batchID))
	logx.Error(ctx, "mini-app Pickup", logx.Any("robotID", robotID))
	saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
	if saleAdminClientErr != nil {
		err = errorx.New("获取零售服务失败r", -1)
		logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
		return data, err
	}
	if batchID == "" {
		err = errorx.New("订单批次号不能为空", -1)
		return data, err
	}

	logx.Error(ctx, "mini-app Pickup", logx.Any("OrderList", ""))
	// 订单是否存在
	list, listCount, _ := dao.Order{}.OrderList(ctx, dal.Q, openid, "", batchID,
		"", goodsIDs, define.OrderStatusToPickUp, -1, -1) // 自提订单
	if listCount == 0 {
		err = errorx.New("订单信息不存在或订单未在待取货状态(未支付或未核销兑换码)", -1)
		return
	}
	if list[0].PurchaseMethod == 2 {
		err = errorx.New("该订单为快递到家，不需要取货码", -1)
		return
	}
	for _, v := range list {
		if v.PickupStatus == 1 {
			err = errorx.New("商品已经取货,请勿重复操作", -1)
			return
		}
	}
	orderInfo := list[0] // 暂时只考虑单个商品订单
	// 是否已经有取货码
	if orderInfo.PickupCode != "" { // 有取货码

		logx.Error(ctx, "mini-app Pickup", logx.Any("orderInfo.PickupCode", orderInfo.PickupCode))
		if robotID == "" { // 未换设备
			// 是否取货超时
			if (orderInfo.PickupDeadline != 0 && orderInfo.PickupDeadline < time.Now().Unix()) ||
				orderInfo.PickupStatus == 2 {
				err = errorx.New("超时未取货已超时")
				return
			}

			// 返回取货码
			return PickupInfo{
				PickupDeadline: orderInfo.PickupDeadline,
				PickupCode:     orderInfo.PickupCode,
			}, nil
		} else { // 重新选择了设备
			pickupDeadline := time.Now().Unix() + 60*60*1 // 取货截止时间,1小时后失效
			if config.Config.EnvType == "test" {
				pickupDeadline = time.Now().Unix() + 600
			}
			// 零售平台创单,拿到取货码
			req := sale_adminc.CreateOrderReq{}
			req.RemoteIp = remoteIP
			req.List = append(req.List, &sale_adminc.CreateOrderReq_List{
				OutTradeNo:      batchID,
				ChildOutTradeNo: orderInfo.ID,
				RobotId:         robotID,
				GoodsId:         orderInfo.GoodsID,
				GoodsName:       orderInfo.GoodsName,
				Count:           int32(orderInfo.Count),
				Univalent:       decimal.NewFromFloat(orderInfo.Univalent).Mul(decimal.NewFromInt(100)).IntPart(),
				ExpiredAt:       pickupDeadline,
			})
			orderRsp, orderErr := saleAdminClient.CreateOrder(ctx, &req)
			if orderErr != nil {
				logx.Error(ctx, "create order error", logx.Any("orderRsp", orderRsp), logx.Any("orderErr", orderErr))
				return
			}
			if orderRsp.Code != 0 {
				err = errorx.New(orderRsp.Msg, int(orderRsp.Code))
				return
			}

			logx.Error(ctx, "mini-app Pickup", logx.Any("CheckOrder", ""))
			req2 := sale_adminc.CheckOrderReq{}
			req2.Openid = orderInfo.Openid
			req2.Orderid = orderInfo.BatchID
			req2.Points = float32(orderInfo.Points)
			orderRsp2, orderErr2 := saleAdminClient.CheckOrder(ctx, &req2)
			if orderErr2 != nil {
				logx.Error(ctx, "check order error", logx.Any("orderRsp", orderRsp2), logx.Any("orderErr", orderErr2))
				return
			}
			if orderRsp2.Code != 0 {
				err = errorx.New(orderRsp2.Msg, int(orderRsp2.Code))
				return
			}

			PickupCode := orderRsp.Data[0].Codes
			// 更新订单表的取货信息
			mysql.NewDB().Model(&models.Order{}).
				Where("batch_id = ?", batchID).
				Where("goods_id = ?", orderInfo.GoodsID).
				Where("purchase_method = 1").
				Where("(pickup_status = 0 || pickup_status = 2)"). // 未取货或取货超时
				Updates(map[string]interface{}{
					"pickup_status":   0,
					"pickup_code":     PickupCode,
					"pickup_deadline": pickupDeadline,
					"robot_id":        robotID,
					// "points_value":    orderInfo.Points,
				})
			return PickupInfo{
				PickupDeadline: pickupDeadline,
				PickupCode:     PickupCode,
			}, nil
		}
	} else { // 无取货码，当前机器取货

		logx.Error(ctx, "mini-app Pickup", logx.Any("orderInfo.PickupCode", orderInfo.PickupCode))

		pickupDeadline := time.Now().Unix() + 60*60*1 // 取货截止时间,1小时后失效
		if config.Config.EnvType == "test" {
			pickupDeadline = time.Now().Unix() + 600
		}
		// 零售平台创单,拿到取货码
		req := sale_adminc.CreateOrderReq{}
		req.RemoteIp = remoteIP
		req.List = append(req.List, &sale_adminc.CreateOrderReq_List{
			OutTradeNo:      batchID,
			ChildOutTradeNo: orderInfo.ID,
			RobotId:         orderInfo.RobotID,
			GoodsId:         orderInfo.GoodsID,
			GoodsName:       orderInfo.GoodsName,
			Count:           int32(orderInfo.Count),
			Univalent:       decimal.NewFromFloat(orderInfo.Univalent).Mul(decimal.NewFromInt(100)).IntPart(),
			ExpiredAt:       pickupDeadline,
		})
		orderRsp, orderErr := saleAdminClient.CreateOrder(ctx, &req)
		if orderErr != nil {
			err = errorx.New("创建订单失败", -1)
			logx.Error(ctx, "create order error", logx.Any("orderRsp", orderRsp), logx.Any("orderErr", orderErr))
			return data, err
		}
		if orderRsp.Code != 0 {
			err = errorx.New(orderRsp.Msg, int(orderRsp.Code))
			return
		}

		logx.Error(ctx, "mini-app Pickup", logx.Any("CheckOrder", ""))
		req2 := sale_adminc.CheckOrderReq{}
		req2.Openid = orderInfo.Openid
		req2.Orderid = orderInfo.BatchID
		req2.Points = float32(orderInfo.Points)
		orderRsp2, orderErr2 := saleAdminClient.CheckOrder(ctx, &req2)
		if orderErr2 != nil {
			logx.Error(ctx, "check order error", logx.Any("orderRsp", orderRsp2), logx.Any("orderErr", orderErr2))
			return
		}
		if orderRsp2.Code != 0 {
			err = errorx.New(orderRsp2.Msg, int(orderRsp2.Code))
			return
		}

		PickupCode := orderRsp.Data[0].Codes
		// 更新订单表的取货信息
		mysql.NewDB().Model(&models.Order{}).
			Where("batch_id = ?", batchID).
			Where("goods_id = ?", orderInfo.GoodsID).
			Where("purchase_method = 1").
			Where("(pickup_status = 0 || pickup_status = 2)"). // 未取货或取货超时
			Updates(map[string]interface{}{
				"pickup_status":   0,
				"pickup_code":     PickupCode,
				"pickup_deadline": pickupDeadline,
			})

		return PickupInfo{
			PickupDeadline: pickupDeadline,
			PickupCode:     PickupCode,
		}, nil
	}
}

type CartGoodsList struct {
	ID             int64   `json:"id"`
	ClassID        string  `json:"class_id"`
	GoodsName      string  `json:"goods_name"`
	ContainerNum   string  `json:"container_num"`
	Univalent      float64 `json:"univalent"`
	GoodsID        string  `json:"goods_id"`        // 商品id
	GoodsType      int64   `json:"goods_type"`      // 商品类型
	GoodsImage     string  `json:"goods_image"`     // 商品图片
	Points         float64 `json:"points"`          // 抵扣积分
	PointsSupport  int64   `json:"points_support"`  // 是否支持积分
	DeductionLimit float64 `json:"deduction_limit"` // 抵扣上限【元】
	RobotName      string  `json:"robot_name"`
}

// 零售获取购物车商品信息
func (Order) SaCartGoods(ctx context.Context, orderID string) (totalAmount float64, list []CartGoodsList, err error) {
	saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
	if saleAdminClientErr != nil {
		logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
		return
	}
	res, _ := saleAdminClient.GetCartGoods(ctx, &sale_adminc.GetCartGoodsReq{OrderId: orderID})
	if res.Code != 0 {
		err = errorx.New(res.Msg, int(res.Code))
		return
	}
	totalAmount = decimal.NewFromInt(res.TotalAmount).Div(decimal.NewFromInt(100)).InexactFloat64() // 分变元
	list = []CartGoodsList{}
	for _, v := range res.List {
		list = append(list, CartGoodsList{
			ID:             v.Id,
			ClassID:        v.ClassId,
			GoodsName:      v.GoodsName,
			ContainerNum:   v.ContainerNum,
			Univalent:      decimal.NewFromInt(v.Univalent).Div(decimal.NewFromInt(100)).InexactFloat64(), // 分变元
			GoodsID:        v.GoodsId,
			GoodsType:      v.GoodsType,
			GoodsImage:     v.GoodsImage,
			Points:         float64(v.Points),
			PointsSupport:  v.PointsSupport,
			DeductionLimit: float64(v.DeductionLimit),
			RobotName:      v.RobotName,
		})
	}
	return totalAmount, list, nil
}

// 零售微信支付
func (Order) SaWechatJSPay(ctx context.Context, orderID, openid string, points float64, points_id string, pay_univalent float64, mall_code string) (data *sale_adminc.WechatJSPayRsp_Data, err error) {

	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("orderID", orderID))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("openid", openid))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("points", fmt.Sprint(points)))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("points_id", points_id))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("pay_univalent", fmt.Sprint(pay_univalent)))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("mall_code", mall_code))

	saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
	if saleAdminClientErr != nil {
		logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
		return
	}
	detail, _ := saleAdminClient.OrderDetail(ctx, &sale_adminc.OrderDetailReq{OutTradeNo: orderID})
	if detail.Code != 0 {
		err = errorx.New(detail.Msg, int(detail.Code))
		return
	}
	if detail.Data == nil {
		err = errorx.New("订单信息不存在", -1)
		return
	}
	if detail.Data.OutTradeNo != "" {
		goodsInfo, _ := saleAdminClient.GoodsDetail(ctx, &sale_adminc.GoodsDetailReq{
			GoodsId: detail.Data.GoodsId,
		})
		if goodsInfo.GoodsType == 6 { // 6 0元购商品
			// 0元购商品限制单人仅限购一单
			var GoodsCount int64
			mysql.NewDB().Model(&models.Order{}).
				WithContext(ctx).
				Where("openid = ?", openid).
				Where("goods_type = 6").
				Where("order_status != ?", define.OrderStatusUnpaid). // 待支付
				Where("order_status != ?", define.OrderStatusRefund). // 已退款
				Count(&GoodsCount)
			if GoodsCount > 0 {
				err = errorx.New("仅限领取一次", -1)
				return
			}
		}
	}
	res, _ := saleAdminClient.WechatJSPay(ctx, &sale_adminc.WechatJSPayReq{
		OrderId: orderID,
		Openid:  openid,
		Points:  float32(points),
		Wapp:    1, // 小程序支付
	})

	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("res.Code", res.Code))
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("res.Msg", res.Msg))
	if res.Code != 0 {
		err = errorx.New(res.Msg, int(res.Code))
		return
	}
	orderInfo, _ := dao.Order{}.Detail(ctx, dal.Q, openid, "", orderID, "", "", 0)

	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("orderInfo.ID", orderInfo.ID))
	// 支付完成之后判断是否要新增订单信息
	if orderInfo == nil || orderInfo.ID == "" {
		if detail.Data.OutTradeNo != "" {
			orderDetail := detail.Data
			var orderStatus int = 0
			if orderDetail.OrderStatus == 1 { // 创单
				orderStatus = define.OrderStatusUnpaid
			} else if orderDetail.OrderStatus == 2 { // 已支付
				orderStatus = define.OrderStatusToPickUp
			} else if lo.Contains([]int32{4, 5, 7}, orderDetail.OrderStatus) { // 4出货成功 5出货失败 7错误
				orderStatus = define.OrderStatusDone
			} else if orderDetail.OrderStatus == 6 { // 已退款
				orderStatus = define.OrderStatusRefund
			}
			univalent := decimal.NewFromInt(orderDetail.Univalent).Div(decimal.NewFromInt(100)).InexactFloat64()
			dao.Order{}.Create(
				ctx,
				dal.Q,
				openid,
				orderDetail.ChildOutTradeNo,
				orderID,
				orderDetail.RobotId,
				orderStatus,
				orderDetail.GoodsId,
				int(orderDetail.GoodsType),
				orderDetail.GoodsName,
				1, univalent, univalent, 0, 0, 1,
				"", models.UserAddress{},
			)
		}
	}
	logx.Info(context.Background(), "SaWechatJSPay", logx.Any("res.Data.SignType", res.Data.SignType))
	if res.Data.SignType == "" {
		res.Data.SignType = "RSA"
	}
	return res.Data, nil
}

// 微信支付
func (Order) WechatJSPay(ctx context.Context, orderID, openid string, points float64, points_id string, pay_univalent float64, mall_code string) (data *wechat.JSAPIPayParams, err error) {
	var orderDetail struct {
		models.Order
		TotalAmount float64 `json:"total_amount"`
	}
	mysql.NewDB().Model(&models.Order{}).
		Where("batch_id = ?", orderID).
		Where("openid = ?", openid).
		Select("*,sum(amount) as total_amount").
		Take(&orderDetail)
	if orderDetail.ID == "" {
		err = errorx.New("订单不存在", -1)
		return
	}
	// 普通订单，15分钟的支付时间
	if orderDetail.OnlineEventID == "" && orderDetail.CreatedAt < time.Now().Unix()-15*60 {
		return nil, errorx.New("订单已关闭", -1)
	} else if orderDetail.OnlineEventID != "" && orderDetail.CreatedAt < time.Now().Unix()-3600*24*7 { // 活动订单，支付超时时间为1周
		return nil, errorx.New("订单已关闭", -1)
	}
	// 待支付或待支付运费
	if orderDetail.OrderStatus != define.OrderStatusUnpaid && orderDetail.OrderStatus != define.OrderStatusUnPaidExpreeFee {
		err = errorx.New("当前订单状态不支持支付", -1)
		return
	}
	if orderDetail.TotalAmount+orderDetail.ExpressFee <= 0 {
		err = errorx.New("订单金额错误", -1)
		return
	}
	// 微信支付初始化
	pkg.WechatPay{}.V3ConfigInit("", "", "")
	if points == 0 && points_id == "" && pay_univalent == 0 && mall_code == "" {
		paymentAmount := decimal.NewFromFloat(orderDetail.TotalAmount + orderDetail.ExpressFee).Mul(decimal.NewFromInt(100)).IntPart() //订单金额【分】
		jsapiRsp, err := pkg.WechatPay{}.V3TransactionJsapi(ctx, orderID, paymentAmount, openid, "派样小程序")
		if err != nil {
			logx.Error(ctx, "订单微信支付错误", logx.Any("订单号", orderDetail.ID), logx.Any("error", err))
		}
		return jsapiRsp, err
	} else {
		saleAdminClient, saleAdminClientErr := client.GetSaleAdminClient()
		if saleAdminClientErr != nil {
			logx.Error(ctx, "connect sale admin grpc error", logx.Any("saleAdminClientErr", saleAdminClientErr))
			return
		}
		if pay_univalent == 0 {
			// orderDetail, _ := saleAdminClient.OrderDetail(ctx, &sale_adminc.OrderDetailReq{
			// 	OutTradeNo: orderID,
			// })
		}
		paymentAmount := int64(math.Round(pay_univalent * 100))
		logx.Info(context.Background(), "WechatJSPay", logx.Any("pay_univalent", fmt.Sprint(paymentAmount)))
		logx.Info(context.Background(), "WechatJSPay", logx.Any("before V3TransactionJsapi", ""), logx.Any("pkg.WechatPay{}", pkg.WechatPay{}))
		jsapiRsp, err := pkg.WechatPay{}.V3TransactionJsapi(ctx, orderID, paymentAmount, openid, "")
		if err != nil {
			logx.Error(ctx, "订单微信支付错误", logx.Any("订单号", orderDetail.ID), logx.Any("error", err))
			return jsapiRsp, err
		}

		logx.Info(context.Background(), "WechatJSPay", logx.Any("after V3TransactionJsapi", ""))
		if jsapiRsp == nil {
			logx.Error(ctx, "WechatJSPay", logx.Any("jsapiRsp", " is nil"))
			return nil, errorx.New("订单微信支付错误")
		}

		if points > 0 {
			res, _ := saleAdminClient.PointsConsume(ctx, &sale_adminc.PointsConsumeReq{
				Points:   float32(points),
				PointsId: points_id,
				Orderid:  orderID,
				Openid:   openid,
				Mallcode: mall_code,
			})
			logx.Error(ctx, "WechatJSPay", logx.Any("res.Code", res.Code))
			logx.Error(ctx, "WechatJSPay", logx.Any("res.Msg", res.Msg))

			dao.Order{}.OrderPointsChange(ctx, dal.Q, "", orderID, points)
		}
		if jsapiRsp == nil {
			return nil, errorx.New("订单微信支付错误")
		}
		return jsapiRsp, err
	}
}

func (Order) StatusStatistics(
	ctx context.Context,
	openid string,
) map[string]int64 {
	var totalOnlineEventOrder, totalUnpaidOrder, totalUnpickedOrder, totalPendingReviewOrder int64
	list := dao.Order{}.StatusStatistics(ctx, mysql.NewDB(), openid)
	for _, record := range list {
		if record.OrderStatus == define.OrderStatusApplyingPending {
			totalOnlineEventOrder = record.Count
		} else if record.OrderStatus == define.OrderStatusUnpaid {
			totalUnpaidOrder = record.Count
		} else if record.OrderStatus == define.OrderStatusToPickUp {
			totalUnpickedOrder = record.Count
		} else if record.OrderStatus == define.OrderStatusPendingReview {
			totalPendingReviewOrder = record.Count
		}
	}
	return map[string]int64{
		"applying_pending_order": totalOnlineEventOrder,   // 待揭晓的订单
		"unpaid_order":           totalUnpaidOrder,        // 待支付的订单
		"unpicked_order":         totalUnpickedOrder,      // 待取货的订单
		"pending_review_order":   totalPendingReviewOrder, // 待评测的订单
	}
}

func (Order) UploadShippingInfo(payerOpenId, outTradeNo string) error {
	// 小程序发货信息录入
	mp, err := pkg.MiniProgram{}.New()
	if err != nil {
		return err
	}
	timeNow := time.Now()
	var shipping = &order.UploadShippingInfoRequest{
		OrderKey: &order.ShippingOrderKey{
			OrderNumberType: 1, // 商户侧单号
			Mchid:           config.Config.WechatV3Config.MchID,
			OutTradeNo:      outTradeNo,
		},
		LogisticsType: order.LogisticsTypeSelfPickup,     // 用户自提
		DeliveryMode:  order.DeliveryModeUnifiedDelivery, //同一发货
		ShippingList: []*order.ShippingInfo{
			{
				ItemDesc: "大道售卖商品",
			},
		},
		UploadTime: &timeNow,
		Payer: &order.ShippingPayer{
			Openid: payerOpenId,
		},
	}
	err = mp.GetShipping().UploadShippingInfo(shipping)
	return err
}
